Let’s import at all of the data that Simon and Matt pulled:
library(tidyverse)
Registered S3 method overwritten by 'dplyr':
method from
print.rowwise_df
[37m── [1mAttaching packages[22m ─────────────────────────────────────────── tidyverse 1.2.1 ──[39m
[37m[32m✔[37m [34mggplot2[37m 3.2.1 [32m✔[37m [34mpurrr [37m 0.3.2
[32m✔[37m [34mtibble [37m 2.1.3 [32m✔[37m [34mdplyr [37m 0.8.3
[32m✔[37m [34mtidyr [37m 1.0.0 [32m✔[37m [34mstringr[37m 1.4.0
[32m✔[37m [34mreadr [37m 1.3.1 [32m✔[37m [34mforcats[37m 0.4.0[39m
[37m── [1mConflicts[22m ────────────────────────────────────────────── tidyverse_conflicts() ──
[31m✖[37m [34mdplyr[37m::[32mfilter()[37m masks [34mstats[37m::filter()
[31m✖[37m [34mdplyr[37m::[32mlag()[37m masks [34mstats[37m::lag()[39m
library(gridExtra)
Attaching package: ‘gridExtra’
The following object is masked from ‘package:dplyr’:
combine
library(maps)
Attaching package: ‘maps’
The following object is masked from ‘package:purrr’:
map
library(ggthemes)
DeltasClean <- read_csv("../data/out/deltas_clean.csv")
Parsed with column specification:
cols(
Delta = [31mcol_character()[39m,
location = [31mcol_character()[39m,
surface = [31mcol_character()[39m,
year = [32mcol_double()[39m,
month = [32mcol_double()[39m,
ndvi = [32mcol_double()[39m,
red = [32mcol_double()[39m,
evi = [32mcol_double()[39m,
savi = [32mcol_double()[39m,
gr = [32mcol_double()[39m,
ndssi = [32mcol_double()[39m
)
DeltaLocations <- read_csv("../data/DeltaLocations.csv")
Parsed with column specification:
cols(
Deltas = [31mcol_character()[39m,
Lat = [32mcol_double()[39m,
Lon = [32mcol_double()[39m
)
As a reminder, for each of the 47 deltas there are measurements of Land & Water areas at Upstream, Downstream and ‘Middle’ locations on the delta. We first lump all the observations together, and look to see which Deltas have many observations:
#counts per delta
DeltaCounts <- count(DeltasClean, Delta)
DeltaCounts
Now, by each month.. where the colorbar represents the number of observations (n) for each month for a given delta:
DeltaObsPerMonth <- count(DeltasClean, Delta, month)
ggplot(DeltaObsPerMonth, aes(y = Delta, x = month, fill=n)) + geom_tile() +
scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
expand_limits(x = c(1,12)) +
scale_fill_gradient( trans = 'log' )

In the above heat map, dark colors (and no color) represent data paucity (and data gaps). Deltas with light colors (e.g., the Parana, Nile, Ebro, Colorado, Brahmani) have lots of data, spread out through the months of the year.
I’ll remove/subset the deltas with sparse coverage (specifically, months with no coverage)….
# need 10 data points per month for NDSSI and NDVI
EnoughObsPerMonth <- DeltasClean %>%
count(Delta, month, surface) %>%
group_by(surface) %>%
filter( n >= 5)
#find deltas missing a given month of observations
DeltaMonthCounts <- EnoughObsPerMonth %>%
ungroup() %>%
count(Delta)
# need 12 months of water and land obs, so 24 mo total
EnoughMonths <- DeltaMonthCounts %>%
filter( n == 24)
CompleteObsDeltas <- pull(EnoughMonths, Delta)
#remove them
DeltasCleaner <- DeltasClean %>%
filter(Delta %in% CompleteObsDeltas)
#add the real dates in month date format
DeltasCleaner$date <- as.Date(paste(DeltasCleaner$year, DeltasCleaner$month, "01", sep="-"), "%Y-%m-%d")
#remove intermediate data
rm(CompleteObsDeltas, EnoughMonths, DeltaMonthCounts)
#EnoughMonths
and extract some metrics; specifically I will make a timeseries of NDVI and NDSSI for each delta using the mean value for each month.
From a quick look at the dat (not shown until much later in the doc):
- Peaks in NDVI are not always correlated with peaks in NDSSI. in other words, maximums in sed concentration are not during maximums in standing biomass.
- The peaks in both timeseries shift around depending on the delta:
- look at the correlation in the Nile.
- The anticorrelation in the Ebro,
- The slight phase shift in the Parana.
#take the mean NDVI and NDSSI for each month, for each delta
DeltaMeans <- DeltasCleaner %>%
group_by(Delta, month, surface) %>%
summarize(MeanNDVI = mean(ndvi, na.rm = TRUE), MeanNDSSI = mean(ndssi, na.rm = TRUE))
#make a 9 column data frame with:
#delta,
#max and min NDVI month,
#NDSSI max and min month,
#max and min values for both NDVI and NDSSI
#####
DeltaMaxNDVI <-
DeltaMeans %>%
filter(surface == 'Land') %>%
select (-c(MeanNDSSI)) %>%
group_by(Delta) %>%
slice(which.max(MeanNDVI)) %>%
rename(MaxMeanNDVImonth = month, MaxMeanNDVI = MeanNDVI)
DeltaMaxNDSSI <-
DeltaMeans %>%
filter(surface == 'Water') %>%
select (-c(MeanNDVI)) %>%
group_by(Delta) %>%
slice(which.max(MeanNDSSI)) %>%
rename(MaxMeanNDSSImonth = month, MaxMeanNDSSI = MeanNDSSI)
DeltaMinNDVI <-
DeltaMeans %>%
filter(surface == 'Land') %>%
select (-c(MeanNDSSI)) %>%
group_by(Delta) %>%
slice(which.min(MeanNDVI)) %>%
rename(MinMeanNDVImonth = month, MinMeanNDVI = MeanNDVI)
DeltaMinNDSSI <-
DeltaMeans %>%
filter(surface == 'Water') %>%
select (-c(MeanNDVI)) %>%
group_by(Delta) %>%
slice(which.min(MeanNDSSI)) %>%
rename(MinMeanNDSSImonth = month, MinMeanNDSSI = MeanNDSSI)
#join into 1 dataframe
DeltaMaxMin <- left_join(DeltaMaxNDVI, DeltaMaxNDSSI, by = 'Delta') %>%
left_join(.,DeltaMinNDVI, by = 'Delta') %>%
left_join(.,DeltaMinNDSSI, by = 'Delta')
#remove intermediate data
rm(DeltaMaxNDVI, DeltaMaxNDSSI, DeltaMinNDSSI,DeltaMinNDVI)
And now we can look at the phase shifts between these two time series (the timeseries of mean NDVI and mean NDSSI). Here are the phase shifts (in month) for each delta:
#compare offset
DeltaMaxMin <- mutate(DeltaMaxMin,
MinOffset = if_else(abs(MinMeanNDVImonth - MinMeanNDSSImonth) > 6,
12 - abs(MinMeanNDVImonth - MinMeanNDSSImonth),
abs(MinMeanNDVImonth - MinMeanNDSSImonth)),
MaxOffset = if_else(abs(MaxMeanNDVImonth - MaxMeanNDSSImonth) > 6,
12 - abs(MaxMeanNDVImonth - MaxMeanNDSSImonth),
abs(MaxMeanNDVImonth - MaxMeanNDSSImonth)),
OffsetDiff = abs(MaxOffset - MinOffset),
rangeNDVI = (MaxMeanNDVI - MinMeanNDVI),
rangeNDSSI = (MaxMeanNDSSI - MinMeanNDSSI))
# DeltaMaxMin <-
# DeltaMaxMin %>%
# select (c(Delta, MinOffset, MaxOffset, OffsetDiff, rangeNDVI, rangeNDSSI,MaxMeanNDSSI,MinMeanNDSSI,MaxMeanNDVI,MinMeanNDVI))
DeltaMaxMin
ggplot(DeltaMaxMin, aes(y = Delta, x = MaxOffset)) + geom_point() +
scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
expand_limits(x = c(0,6)) +
ggtitle("MaxOffset")

ggplot(DeltaMaxMin, aes(y = Delta, x = MinOffset)) + geom_point() +
scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
expand_limits(x = c(0,6)) +
ggtitle("MinOffset")

ggplot(DeltaMaxMin, aes(y = Delta, x = OffsetDiff)) + geom_point() +
scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
expand_limits(x = c(0,6)) +
ggtitle("Offset Difference")

And here are the phase shifts/offsets against other measured parameters for each delta. The range, max and mean of NDVI and NDSSI is calculated from the timeseries, so it is really the max, min, and range of the monthly means (i.e., the maximum of the means, the minimum of the means, and the range of the mean). Offset is measured in months.
slice1 <- ggplot(DeltaMaxMin, aes(y = rangeNDVI, x = MaxOffset)) + geom_point()
# + scale_x_discrete(limits = c(1:6), breaks = c(1:6)) + expand_limits(x = c(1,6))
slice2 <- ggplot(DeltaMaxMin, aes(y = rangeNDVI, x = rangeNDSSI)) + geom_point()
slice3 <- ggplot(DeltaMaxMin, aes(y = rangeNDSSI, x = MaxOffset)) + geom_point()
slice4 <- ggplot(DeltaMaxMin, aes(y = MaxMeanNDVI, x = rangeNDVI)) + geom_point()
slice5 <- ggplot(DeltaMaxMin, aes(y = MaxMeanNDVI, x = rangeNDSSI)) + geom_point()
slice6 <- ggplot(DeltaMaxMin, aes(y = MaxMeanNDVI, x = MaxOffset)) + geom_point()
slice7 <- ggplot(DeltaMaxMin, aes(y = MaxMeanNDSSI, x = MaxMeanNDVI)) + geom_point()
slice8 <- ggplot(DeltaMaxMin, aes(y = MaxMeanNDSSI, x = rangeNDSSI)) + geom_point()
slice9 <- ggplot(DeltaMaxMin, aes(y = MaxMeanNDSSI, x = MaxOffset)) + geom_point()
slice10 <- ggplot(DeltaMaxMin, aes(y = MaxMeanNDSSI, x = rangeNDVI)) + geom_point()
grid.arrange(slice1, slice2, slice3, slice4, slice5, slice6, ncol=3)

grid.arrange(slice7, slice8, slice9, slice10, ncol=2)

#remove those panels
rm(slice1, slice2, slice3, slice4, slice5, slice6,slice7, slice8, slice9, slice10)
Join Latitude and longitude data
DeltaDatawLocations <- left_join(DeltaMaxMin, DeltaLocations, by = c("Delta" = "Deltas"))
DeltaDatawLocations <- DeltaDatawLocations %>%
mutate(Absolute_Latitude= abs(Lat))
plot params vs lat
loc1 <- ggplot(DeltaDatawLocations, aes(y = Absolute_Latitude, x = MaxOffset)) + geom_point()
loc2 <- ggplot(DeltaDatawLocations, aes(y = Absolute_Latitude, x = rangeNDSSI)) + geom_point()
loc3 <- ggplot(DeltaDatawLocations, aes(y = Absolute_Latitude, x = rangeNDVI)) + geom_point()
loc4 <- ggplot(DeltaDatawLocations, aes(y = Absolute_Latitude, x = MaxMeanNDVI)) + geom_point()
loc5 <- ggplot(DeltaDatawLocations, aes(y = Absolute_Latitude, x = MaxMeanNDSSI)) + geom_point()
grid.arrange(loc1, loc2, loc3, loc4, loc5, ncol=2)

loc1
ggsave("loc1.pdf", width = 4, height = 4)

loc2
ggsave("loc2.pdf", width = 4, height = 4)

loc3
ggsave("loc3.pdf", width = 4, height = 4)

#remove those panels
rm(loc1, loc2, loc3, loc4, loc5)
#find the linear model
DeltaOffset_lm <- lm( Absolute_Latitude ~ MaxOffset, data = DeltaDatawLocations)
summary(DeltaOffset_lm)
Call:
lm(formula = Absolute_Latitude ~ MaxOffset, data = DeltaDatawLocations)
Residuals:
Min 1Q Median 3Q Max
-28.4079 -5.0293 -0.1842 10.6492 18.1549
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 16.919 3.993 4.237 0.00021 ***
MaxOffset 2.819 1.099 2.564 0.01578 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 11.94 on 29 degrees of freedom
Multiple R-squared: 0.1848, Adjusted R-squared: 0.1567
F-statistic: 6.576 on 1 and 29 DF, p-value: 0.01578
ggplot(DeltaDatawLocations, aes(x = Absolute_Latitude, y = MaxOffset, color = rangeNDSSI)) + geom_point() +
scale_color_gradient(low = "yellow", high = "red", na.value = NA) +
geom_smooth(mapping = aes(x = Absolute_Latitude, y = MaxOffset, ), method=lm )
ggsave("NDSSIoffset.pdf", width = 6, height = 4)

ggplot(DeltaDatawLocations, aes(x = Absolute_Latitude, y = MaxOffset, color = rangeNDVI)) + geom_point() +
scale_color_gradient(low = "yellow", high = "red", na.value = NA) +
geom_smooth(mapping = aes(x = Absolute_Latitude, y = MaxOffset, ), method=lm )
ggsave("NDVIoffset.pdf", width = 6, height = 4)

Now for the maps:
world <- ggplot() +
borders("world", colour = "gray85", fill = "gray80") +
theme_map()
DeltaOffsetMap <- world +
geom_point(aes(x = Lon, y = Lat, color = MaxOffset),
data = DeltaDatawLocations,
size = 5) + scale_color_gradient( high = "red", low = "yellow") +
ggtitle("Offset Between NDVI peak on Land and NDSSI peak in water")
DeltaOffsetMap
ggsave("DeltaOffsetMap.pdf", width = 6, height = 4)

world <- ggplot() +
borders("world", colour = "gray85", fill = "gray80") +
theme_map()
DeltaNDVIrangeMap <- world +
geom_point(aes(x = Lon, y = Lat, color = rangeNDVI),
data = DeltaDatawLocations,
size = 5) + scale_color_gradient( high = "red", low = "yellow") +
ggtitle("NDVI range")
DeltaNDSSIrangeMap <- world +
geom_point(aes(x = Lon, y = Lat, color = rangeNDSSI),
data = DeltaDatawLocations,
size = 5) + scale_color_gradient( high = "red", low = "yellow") + ggtitle("NDSSI range")
DeltaNDVIrangeMap
ggsave("DeltaNDVIrangeMap.pdf", width = 6, height = 4)

DeltaNDSSIrangeMap
ggsave("DeltaNDSSIrangeMap.pdf", width = 6, height = 4)

Let’s look at some of the data. To quantify the water, we use NDSSI. to quantify land, we use NDVI.here is the time series for the Parana.
DeltaPlotter <- function(DeltaName) {
#Counts each month
numVeg <- DeltasCleaner %>%
select(Delta, surface, month, ndvi) %>%
filter(Delta == DeltaName & surface == "Land" & !is.na(ndvi)) %>%
group_by(month) %>%
summarize(n = n())
numSed <- DeltasCleaner %>%
select(Delta, surface, month, ndssi) %>%
filter(Delta == DeltaName &
surface == "Water" & !is.na(ndssi)) %>%
group_by(month) %>%
summarize(n = n())
#Highlight the Maximum and Minimum Month for each delta, NDVI and NDSSI
#LAND
Veg <-
ggplot(data = filter(DeltasCleaner, Delta == DeltaName &
surface == "Land")) +
geom_boxplot(aes(x = month, y = ndvi, group = month)) +
scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
expand_limits(x = c(1, 12), y = c(0, 1)) +
ggtitle(DeltaName) +
geom_text(data = numVeg, aes(y = 1.05, x = month, label = n)) +
geom_boxplot(
data = filter(
DeltasCleaner,
Delta == DeltaName &
surface == "Land" & month == DeltaMaxMin$MaxMeanNDVImonth[DeltaMaxMin$Delta == DeltaName]
),
aes(x = month, y = ndvi, group = month),
fill = "green"
) +
geom_boxplot(
data = filter(
DeltasCleaner,
Delta == DeltaName &
surface == "Land" & month == DeltaMaxMin$MinMeanNDVImonth[DeltaMaxMin$Delta ==DeltaName]
),
aes(x = month, y = ndvi, group = month),
fill = "blue"
)
Sed <-
ggplot(data = filter(DeltasCleaner, Delta == DeltaName &
surface == "Water")) +
geom_boxplot(aes(x = month, y = ndssi, group = month)) +
scale_x_discrete(limits = c(1:12), breaks = c(1:12)) +
expand_limits(x = c(1, 12), y = c(0, 1)) +
geom_text(data = numSed, aes(y = 1.05, x = month, label = n)) +
geom_boxplot(
data = filter(
DeltasCleaner,
Delta == DeltaName &
surface == "Water" & month == DeltaMaxMin$MaxMeanNDSSImonth[DeltaMaxMin$Delta == DeltaName]
),
aes(x = month, y = ndssi, group = month),
fill = "green"
) +
geom_boxplot(
data = filter(
DeltasCleaner,
Delta == DeltaName &
surface == "Water" & month == DeltaMaxMin$MinMeanNDSSImonth[DeltaMaxMin$Delta == DeltaName]
),
aes(x = month, y = ndssi, group = month),
fill = "blue"
)
return(grid.arrange(Veg, Sed, nrow = 2))
}
Here is an ‘Offset’ example from the Parana:
DeltaPlotter("Parana")

and now an in-phase example from
DeltaPlotter("Magdalena")

and the Ebro:
DeltaPlotter("Krishna")

and the Tana
DeltaPlotter("Tana")

and the Tana
DeltaPlotter("Godavari")

DeltaPlotter("Krishna")

Now for some work with GRDC data:
#import the data (monthly means for 21 stations)
DeltasGRDC <- read_csv("../data/GRDCstations.csv")
Parsed with column specification:
cols(
Deltas = [31mcol_character()[39m,
GRDC_Station = [32mcol_double()[39m,
Time_Series_Length = [31mcol_character()[39m,
January = [32mcol_double()[39m,
February = [32mcol_double()[39m,
March = [32mcol_double()[39m,
April = [32mcol_double()[39m,
May = [32mcol_double()[39m,
June = [32mcol_double()[39m,
July = [32mcol_double()[39m,
August = [32mcol_double()[39m,
September = [32mcol_double()[39m,
October = [32mcol_double()[39m,
November = [32mcol_double()[39m,
December = [32mcol_double()[39m
)
#calculate the mean of the monthly means
DeltasGRDCwMean <- DeltasGRDC %>%
rowwise() %>%
mutate(MMD=mean(c(January,February,March,April,
May,June,July,August,
September,October,November,December)))
DeltasGRDCwMean <- DeltasGRDCwMean %>%
rowwise() %>%
mutate(Range_Discharge = max(c(January,February,March,April,
May,June,July,August,
September,October,November,December)) -
min(c(January,February,March,April,
May,June,July,August,
September,October,November,December))
)
#join to location data:
DeltawLocGRDC <- left_join(DeltaDatawLocations, DeltasGRDCwMean, by = c("Delta" = "Deltas"))
#plot mean of monthly means against NDSSI
ggplot(DeltawLocGRDC, aes(y = Range_Discharge, x = rangeNDSSI)) + geom_point() + scale_y_continuous(trans='log10')
ggsave("GRDCNDSSI.pdf", width = 6, height = 4)

ggplot(DeltawLocGRDC, aes(y = Range_Discharge, x = MaxMeanNDSSI)) + geom_point() + scale_y_continuous(trans='log10')

#rename the months by numbers and tidy the GRDC data
DeltasDischarge <- DeltasGRDC %>%
rename(Delta = Deltas,"1" = January, "2"= February, "3"= March, "4"= April,
"5"=May, "6"=June, "7"=July, "8"= August, "9" = September, "10"=October,
"11"=November, "12"=December) %>%
select(Delta, "1" , "2" , "3", "4","5", "6", "7", "8", "9", "10", "11", "12") %>%
pivot_longer(-Delta, names_to = "month", values_to = "discharge")
#find max GRDC month for each delta
DeltaMaxDischarge <-
DeltasDischarge %>%
group_by(Delta) %>%
slice(which.max(discharge)) %>%
rename(MaxDischargeMonth = month, MaxDischarge = discharge)
DeltaMaxDischarge$MaxDischargeMonth = as.numeric(DeltaMaxDischarge$MaxDischargeMonth)
#join with other delta data
DeltaMaxMinDischarge <- left_join(DeltaMaxMin, DeltaMaxDischarge, by = 'Delta')
#calculate offset
DeltaMaxMinDischarge <- DeltaMaxMinDischarge %>%
mutate( DissOff = if_else(abs(MaxDischargeMonth - MaxMeanNDSSImonth) > 6,
12 - abs(MaxDischargeMonth - MaxMeanNDSSImonth),
abs(MaxDischargeMonth - MaxMeanNDSSImonth))
)
#Compare offset with NDSSI (deltamaxmin$maxmeanNDSSImonth)
ggplot(DeltaMaxMinDischarge, aes(y = Delta, x = DissOff)) + geom_point() +
scale_x_discrete(limits = c(1:6), breaks = c(1:6)) +
expand_limits(x = c(0,6)) +
ggtitle("DisOffset")

#join lat data
DeltaDatawLocations <- left_join(DeltaMaxMinDischarge, DeltaLocations, by = c("Delta" = "Deltas"))
DeltaDatawLocations <- DeltaDatawLocations %>%
mutate(Absolute_Latitude= abs(Lat))
# plot offset on graph by lat
ggplot(DeltaDatawLocations, aes(x = Absolute_Latitude, y = DissOff)) + geom_point() +
scale_color_gradient(low = "yellow", high = "red", na.value = NA)
#+ geom_smooth(mapping = aes(x = Absolute_Latitude, y = DissOff, ), method=lm )
ggsave("DisOffset.pdf", width = 6, height = 4)

#plot offset on map
DeltaDisOffsetMap <- world +
geom_point(aes(x = Lon, y = Lat, color = DissOff),
data = DeltaDatawLocations,
size = 5) + scale_color_gradient( high = "red", low = "yellow") +
ggtitle("Offset Between GRDC discharge peak and NDSSI peak in water")
DeltaDisOffsetMap

#ggsave("DeltaOffsetMap.pdf", width = 6, height = 4)
LS0tCnRpdGxlOiAiRGVsdGFzIE5vdGVib29rIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKTGV0J3MgaW1wb3J0IGF0IGFsbCBvZiB0aGUgZGF0YSB0aGF0IFNpbW9uIGFuZCBNYXR0IHB1bGxlZDoKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkobWFwcykKbGlicmFyeShnZ3RoZW1lcykKCkRlbHRhc0NsZWFuIDwtIHJlYWRfY3N2KCIuLi9kYXRhL291dC9kZWx0YXNfY2xlYW4uY3N2IikKRGVsdGFMb2NhdGlvbnMgPC0gcmVhZF9jc3YoIi4uL2RhdGEvRGVsdGFMb2NhdGlvbnMuY3N2IikKYGBgCgpBcyBhIHJlbWluZGVyLCBmb3IgZWFjaCBvZiB0aGUgNDcgZGVsdGFzIHRoZXJlIGFyZSBtZWFzdXJlbWVudHMgb2YgTGFuZCAmIFdhdGVyIGFyZWFzIGF0IFVwc3RyZWFtLCBEb3duc3RyZWFtIGFuZCAnTWlkZGxlJyBsb2NhdGlvbnMgb24gdGhlIGRlbHRhLiBXZSBmaXJzdCBsdW1wIGFsbCB0aGUgb2JzZXJ2YXRpb25zIHRvZ2V0aGVyLCBhbmQgbG9vayB0byBzZWUgd2hpY2ggRGVsdGFzIGhhdmUgbWFueSBvYnNlcnZhdGlvbnM6CgpgYGB7cn0KI2NvdW50cyBwZXIgZGVsdGEKRGVsdGFDb3VudHMgPC0gY291bnQoRGVsdGFzQ2xlYW4sIERlbHRhKQpEZWx0YUNvdW50cwpgYGAKCgpOb3csIGJ5IGVhY2ggbW9udGguLiB3aGVyZSB0aGUgY29sb3JiYXIgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyAobikgZm9yIGVhY2ggbW9udGggZm9yIGEgZ2l2ZW4gZGVsdGE6CmBgYHtyfQpEZWx0YU9ic1Blck1vbnRoIDwtIGNvdW50KERlbHRhc0NsZWFuLCBEZWx0YSwgbW9udGgpCgpnZ3Bsb3QoRGVsdGFPYnNQZXJNb250aCwgYWVzKHkgPSBEZWx0YSwgeCA9IG1vbnRoLCBmaWxsPW4pKSArIGdlb21fdGlsZSgpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6MTIpLCBicmVha3MgPSBjKDE6MTIpKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gYygxLDEyKSkgKyAKICBzY2FsZV9maWxsX2dyYWRpZW50KCB0cmFucyA9ICdsb2cnICkKCmBgYAoKCkluIHRoZSBhYm92ZSBoZWF0IG1hcCwgZGFyayBjb2xvcnMgKGFuZCBubyBjb2xvcikgcmVwcmVzZW50IGRhdGEgcGF1Y2l0eSAoYW5kIGRhdGEgZ2FwcykuIERlbHRhcyB3aXRoIGxpZ2h0IGNvbG9ycyAoZS5nLiwgdGhlIFBhcmFuYSwgTmlsZSwgRWJybywgQ29sb3JhZG8sIEJyYWhtYW5pKSBoYXZlIGxvdHMgb2YgZGF0YSwgc3ByZWFkIG91dCB0aHJvdWdoIHRoZSBtb250aHMgb2YgdGhlIHllYXIuCgoKSSdsbCByZW1vdmUvc3Vic2V0IHRoZSBkZWx0YXMgd2l0aCBzcGFyc2UgY292ZXJhZ2UgKHNwZWNpZmljYWxseSwgbW9udGhzIHdpdGggbm8gY292ZXJhZ2UpLi4uLiAKYGBge3J9CgojIG5lZWQgMTAgZGF0YSBwb2ludHMgcGVyIG1vbnRoIGZvciBORFNTSSBhbmQgTkRWSQpFbm91Z2hPYnNQZXJNb250aCA8LSBEZWx0YXNDbGVhbiAlPiUKICBjb3VudChEZWx0YSwgbW9udGgsIHN1cmZhY2UpICU+JSAKICBncm91cF9ieShzdXJmYWNlKSAlPiUKICBmaWx0ZXIoIG4gPj0gNSkKCiNmaW5kIGRlbHRhcyBtaXNzaW5nIGEgZ2l2ZW4gbW9udGggb2Ygb2JzZXJ2YXRpb25zCkRlbHRhTW9udGhDb3VudHMgPC0gRW5vdWdoT2JzUGVyTW9udGggJT4lCiAgdW5ncm91cCgpICU+JQogIGNvdW50KERlbHRhKQoKIyBuZWVkIDEyIG1vbnRocyBvZiB3YXRlciBhbmQgbGFuZCBvYnMsIHNvIDI0IG1vIHRvdGFsCkVub3VnaE1vbnRocyA8LSBEZWx0YU1vbnRoQ291bnRzICU+JQogZmlsdGVyKCBuID09IDI0KQoKQ29tcGxldGVPYnNEZWx0YXMgPC0gcHVsbChFbm91Z2hNb250aHMsIERlbHRhKQoKI3JlbW92ZSB0aGVtCkRlbHRhc0NsZWFuZXIgPC0gRGVsdGFzQ2xlYW4gJT4lCiAgZmlsdGVyKERlbHRhICVpbiUgQ29tcGxldGVPYnNEZWx0YXMpCgojYWRkIHRoZSByZWFsIGRhdGVzIGluIG1vbnRoIGRhdGUgZm9ybWF0CkRlbHRhc0NsZWFuZXIkZGF0ZSA8LSBhcy5EYXRlKHBhc3RlKERlbHRhc0NsZWFuZXIkeWVhciwgRGVsdGFzQ2xlYW5lciRtb250aCwgIjAxIiwgc2VwPSItIiksICIlWS0lbS0lZCIpCgojcmVtb3ZlIGludGVybWVkaWF0ZSBkYXRhCnJtKENvbXBsZXRlT2JzRGVsdGFzLCBFbm91Z2hNb250aHMsIERlbHRhTW9udGhDb3VudHMpCgojRW5vdWdoTW9udGhzCmBgYAoKYW5kIGV4dHJhY3Qgc29tZSBtZXRyaWNzOyBzcGVjaWZpY2FsbHkgSSB3aWxsIG1ha2UgYSB0aW1lc2VyaWVzIG9mIE5EVkkgYW5kIE5EU1NJIGZvciBlYWNoIGRlbHRhIHVzaW5nIHRoZSBtZWFuIHZhbHVlIGZvciBlYWNoIG1vbnRoLgoKRnJvbSBhIHF1aWNrIGxvb2sgYXQgdGhlIGRhdCAobm90IHNob3duIHVudGlsIG11Y2ggbGF0ZXIgaW4gdGhlIGRvYyk6CgoqIFBlYWtzIGluIE5EVkkgYXJlIG5vdCBhbHdheXMgY29ycmVsYXRlZCB3aXRoIHBlYWtzIGluIE5EU1NJLiBpbiBvdGhlciB3b3JkcywgbWF4aW11bXMgaW4gc2VkIGNvbmNlbnRyYXRpb24gYXJlIG5vdCBkdXJpbmcgbWF4aW11bXMgaW4gc3RhbmRpbmcgYmlvbWFzcy4KKiBUaGUgcGVha3MgaW4gYm90aCB0aW1lc2VyaWVzIHNoaWZ0IGFyb3VuZCBkZXBlbmRpbmcgb24gdGhlIGRlbHRhOgogICArIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIGluIHRoZSBOaWxlLgogICArIFRoZSBhbnRpY29ycmVsYXRpb24gaW4gdGhlIEVicm8sCiAgICsgVGhlIHNsaWdodCBwaGFzZSBzaGlmdCBpbiB0aGUgUGFyYW5hLgoKCmBgYHtyICBpbmNsdWRlID0gVFJVRX0KI3Rha2UgdGhlIG1lYW4gTkRWSSBhbmQgTkRTU0kgZm9yIGVhY2ggbW9udGgsIGZvciBlYWNoIGRlbHRhCkRlbHRhTWVhbnMgPC0gRGVsdGFzQ2xlYW5lciAlPiUKICBncm91cF9ieShEZWx0YSwgbW9udGgsIHN1cmZhY2UpICU+JQogIHN1bW1hcml6ZShNZWFuTkRWSSA9IG1lYW4obmR2aSwgbmEucm0gPSBUUlVFKSwgTWVhbk5EU1NJID0gbWVhbihuZHNzaSwgbmEucm0gPSBUUlVFKSkKCiNtYWtlIGEgOSBjb2x1bW4gZGF0YSBmcmFtZSB3aXRoOgojZGVsdGEsIAojbWF4IGFuZCBtaW4gTkRWSSBtb250aCwgCiNORFNTSSBtYXggYW5kIG1pbiBtb250aCwgCiNtYXggYW5kIG1pbiB2YWx1ZXMgZm9yIGJvdGggTkRWSSBhbmQgTkRTU0kKCiMjIyMjCgpEZWx0YU1heE5EVkkgPC0gCiAgRGVsdGFNZWFucyAlPiUgCiAgZmlsdGVyKHN1cmZhY2UgPT0gJ0xhbmQnKSAgJT4lIAogIHNlbGVjdCAoLWMoTWVhbk5EU1NJKSkgJT4lIAogIGdyb3VwX2J5KERlbHRhKSAlPiUgCiAgc2xpY2Uod2hpY2gubWF4KE1lYW5ORFZJKSkgJT4lIAogIHJlbmFtZShNYXhNZWFuTkRWSW1vbnRoID0gbW9udGgsIE1heE1lYW5ORFZJID0gTWVhbk5EVkkpCgpEZWx0YU1heE5EU1NJIDwtIAogIERlbHRhTWVhbnMgJT4lIAogIGZpbHRlcihzdXJmYWNlID09ICdXYXRlcicpICAlPiUgCiAgc2VsZWN0ICgtYyhNZWFuTkRWSSkpICU+JSAKICBncm91cF9ieShEZWx0YSkgJT4lIAogIHNsaWNlKHdoaWNoLm1heChNZWFuTkRTU0kpKSAlPiUgCiAgcmVuYW1lKE1heE1lYW5ORFNTSW1vbnRoID0gbW9udGgsIE1heE1lYW5ORFNTSSA9IE1lYW5ORFNTSSkKCgpEZWx0YU1pbk5EVkkgPC0gCiAgRGVsdGFNZWFucyAlPiUgCiAgZmlsdGVyKHN1cmZhY2UgPT0gJ0xhbmQnKSAgJT4lIAogIHNlbGVjdCAoLWMoTWVhbk5EU1NJKSkgJT4lIAogIGdyb3VwX2J5KERlbHRhKSAlPiUgCiAgc2xpY2Uod2hpY2gubWluKE1lYW5ORFZJKSkgJT4lIAogIHJlbmFtZShNaW5NZWFuTkRWSW1vbnRoID0gbW9udGgsIE1pbk1lYW5ORFZJID0gTWVhbk5EVkkpCgpEZWx0YU1pbk5EU1NJIDwtIAogIERlbHRhTWVhbnMgJT4lIAogIGZpbHRlcihzdXJmYWNlID09ICdXYXRlcicpICAlPiUgCiAgc2VsZWN0ICgtYyhNZWFuTkRWSSkpICU+JSAKICBncm91cF9ieShEZWx0YSkgJT4lIAogIHNsaWNlKHdoaWNoLm1pbihNZWFuTkRTU0kpKSAlPiUgCiAgcmVuYW1lKE1pbk1lYW5ORFNTSW1vbnRoID0gbW9udGgsIE1pbk1lYW5ORFNTSSA9IE1lYW5ORFNTSSkKCgojam9pbiBpbnRvIDEgZGF0YWZyYW1lCkRlbHRhTWF4TWluIDwtIGxlZnRfam9pbihEZWx0YU1heE5EVkksIERlbHRhTWF4TkRTU0ksIGJ5ID0gJ0RlbHRhJykgJT4lIAogIGxlZnRfam9pbiguLERlbHRhTWluTkRWSSwgYnkgPSAnRGVsdGEnKSAlPiUgCiAgbGVmdF9qb2luKC4sRGVsdGFNaW5ORFNTSSwgYnkgPSAnRGVsdGEnKQoKI3JlbW92ZSBpbnRlcm1lZGlhdGUgZGF0YQpybShEZWx0YU1heE5EVkksIERlbHRhTWF4TkRTU0ksIERlbHRhTWluTkRTU0ksRGVsdGFNaW5ORFZJKQoKYGBgCgoKQW5kIG5vdyB3ZSBjYW4gbG9vayBhdCB0aGUgcGhhc2Ugc2hpZnRzIGJldHdlZW4gdGhlc2UgdHdvIHRpbWUgc2VyaWVzICh0aGUgdGltZXNlcmllcyBvZiBtZWFuIE5EVkkgYW5kIG1lYW4gTkRTU0kpLiBIZXJlIGFyZSB0aGUgcGhhc2Ugc2hpZnRzIChpbiBtb250aCkgZm9yIGVhY2ggZGVsdGE6CmBgYHtyIH0KI2NvbXBhcmUgb2Zmc2V0CkRlbHRhTWF4TWluIDwtIG11dGF0ZShEZWx0YU1heE1pbiwgCiAgICAgICAgICAgICAgICAgICAgICBNaW5PZmZzZXQgPSBpZl9lbHNlKGFicyhNaW5NZWFuTkRWSW1vbnRoIC0gTWluTWVhbk5EU1NJbW9udGgpID4gNiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEyIC0gYWJzKE1pbk1lYW5ORFZJbW9udGggLSBNaW5NZWFuTkRTU0ltb250aCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFicyhNaW5NZWFuTkRWSW1vbnRoIC0gTWluTWVhbk5EU1NJbW9udGgpKSwKICAgICAgICAgICAgICAgICAgICAgIE1heE9mZnNldCA9IGlmX2Vsc2UoYWJzKE1heE1lYW5ORFZJbW9udGggLSBNYXhNZWFuTkRTU0ltb250aCkgPiA2LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxMiAtIGFicyhNYXhNZWFuTkRWSW1vbnRoIC0gTWF4TWVhbk5EU1NJbW9udGgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhYnMoTWF4TWVhbk5EVkltb250aCAtIE1heE1lYW5ORFNTSW1vbnRoKSksCiAgICAgICAgICAgICAgICAgICAgICBPZmZzZXREaWZmID0gYWJzKE1heE9mZnNldCAtIE1pbk9mZnNldCksCiAgICAgICAgICAgICAgICAgICAgICByYW5nZU5EVkkgPSAoTWF4TWVhbk5EVkkgLSBNaW5NZWFuTkRWSSksIAogICAgICAgICAgICAgICAgICAgICAgcmFuZ2VORFNTSSA9IChNYXhNZWFuTkRTU0kgLSBNaW5NZWFuTkRTU0kpKQoKIyBEZWx0YU1heE1pbiA8LSAKIyAgIERlbHRhTWF4TWluICAgJT4lCiMgICBzZWxlY3QgKGMoRGVsdGEsIE1pbk9mZnNldCwgTWF4T2Zmc2V0LCBPZmZzZXREaWZmLCByYW5nZU5EVkksIHJhbmdlTkRTU0ksTWF4TWVhbk5EU1NJLE1pbk1lYW5ORFNTSSxNYXhNZWFuTkRWSSxNaW5NZWFuTkRWSSkpIAoKRGVsdGFNYXhNaW4KCmdncGxvdChEZWx0YU1heE1pbiwgYWVzKHkgPSBEZWx0YSwgeCA9IE1heE9mZnNldCkpICsgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6NiksIGJyZWFrcyA9IGMoMTo2KSkgKwogIGV4cGFuZF9saW1pdHMoeCA9IGMoMCw2KSkgICsgCiAgZ2d0aXRsZSgiTWF4T2Zmc2V0IikKCmdncGxvdChEZWx0YU1heE1pbiwgYWVzKHkgPSBEZWx0YSwgeCA9IE1pbk9mZnNldCkpICsgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6NiksIGJyZWFrcyA9IGMoMTo2KSkgKwogIGV4cGFuZF9saW1pdHMoeCA9IGMoMCw2KSkgICsgCiAgZ2d0aXRsZSgiTWluT2Zmc2V0IikKCmdncGxvdChEZWx0YU1heE1pbiwgYWVzKHkgPSBEZWx0YSwgeCA9IE9mZnNldERpZmYpKSArIGdlb21fcG9pbnQoKSArIAogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygxOjYpLCBicmVha3MgPSBjKDE6NikpICsKICBleHBhbmRfbGltaXRzKHggPSBjKDAsNikpICArIAogIGdndGl0bGUoIk9mZnNldCBEaWZmZXJlbmNlIikKCmBgYAoKICAgQW5kIGhlcmUgYXJlIHRoZSBwaGFzZSBzaGlmdHMvb2Zmc2V0cyBhZ2FpbnN0IG90aGVyIG1lYXN1cmVkIHBhcmFtZXRlcnMgZm9yIGVhY2ggZGVsdGEuIFRoZSByYW5nZSwgbWF4IGFuZCBtZWFuIG9mIE5EVkkgYW5kIE5EU1NJIGlzIGNhbGN1bGF0ZWQgZnJvbSB0aGUgdGltZXNlcmllcywgc28gaXQgaXMgcmVhbGx5IHRoZSBtYXgsIG1pbiwgYW5kIHJhbmdlIG9mIHRoZSBtb250aGx5IG1lYW5zIChpLmUuLCB0aGUgbWF4aW11bSBvZiB0aGUgbWVhbnMsIHRoZSBtaW5pbXVtIG9mIHRoZSBtZWFucywgYW5kIHRoZSByYW5nZSBvZiB0aGUgbWVhbikuIE9mZnNldCBpcyBtZWFzdXJlZCBpbiBtb250aHMuCgpgYGB7cn0Kc2xpY2UxIDwtIGdncGxvdChEZWx0YU1heE1pbiwgYWVzKHkgPSByYW5nZU5EVkksIHggPSBNYXhPZmZzZXQpKSArIGdlb21fcG9pbnQoKSAKIyArIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygxOjYpLCBicmVha3MgPSBjKDE6NikpICsgZXhwYW5kX2xpbWl0cyh4ID0gYygxLDYpKSAKCnNsaWNlMiA8LSBnZ3Bsb3QoRGVsdGFNYXhNaW4sIGFlcyh5ID0gcmFuZ2VORFZJLCB4ID0gcmFuZ2VORFNTSSkpICsgZ2VvbV9wb2ludCgpIApzbGljZTMgPC0gZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeSA9IHJhbmdlTkRTU0ksIHggPSBNYXhPZmZzZXQpKSArIGdlb21fcG9pbnQoKSAKCnNsaWNlNCA8LSBnZ3Bsb3QoRGVsdGFNYXhNaW4sIGFlcyh5ID0gTWF4TWVhbk5EVkksIHggPSByYW5nZU5EVkkpKSArIGdlb21fcG9pbnQoKSAKc2xpY2U1IDwtIGdncGxvdChEZWx0YU1heE1pbiwgYWVzKHkgPSBNYXhNZWFuTkRWSSwgeCA9IHJhbmdlTkRTU0kpKSArIGdlb21fcG9pbnQoKSAKc2xpY2U2IDwtIGdncGxvdChEZWx0YU1heE1pbiwgYWVzKHkgPSBNYXhNZWFuTkRWSSwgeCA9IE1heE9mZnNldCkpICsgZ2VvbV9wb2ludCgpIAoKc2xpY2U3IDwtIGdncGxvdChEZWx0YU1heE1pbiwgYWVzKHkgPSBNYXhNZWFuTkRTU0ksIHggPSBNYXhNZWFuTkRWSSkpICsgZ2VvbV9wb2ludCgpIApzbGljZTggPC0gZ2dwbG90KERlbHRhTWF4TWluLCBhZXMoeSA9IE1heE1lYW5ORFNTSSwgeCA9IHJhbmdlTkRTU0kpKSArIGdlb21fcG9pbnQoKSAKc2xpY2U5IDwtIGdncGxvdChEZWx0YU1heE1pbiwgYWVzKHkgPSBNYXhNZWFuTkRTU0ksIHggPSBNYXhPZmZzZXQpKSArIGdlb21fcG9pbnQoKSAKc2xpY2UxMCA8LSBnZ3Bsb3QoRGVsdGFNYXhNaW4sIGFlcyh5ID0gTWF4TWVhbk5EU1NJLCB4ID0gcmFuZ2VORFZJKSkgKyBnZW9tX3BvaW50KCkgCgpncmlkLmFycmFuZ2Uoc2xpY2UxLCBzbGljZTIsIHNsaWNlMywgc2xpY2U0LCBzbGljZTUsIHNsaWNlNiwgbmNvbD0zKQpncmlkLmFycmFuZ2Uoc2xpY2U3LCBzbGljZTgsIHNsaWNlOSwgc2xpY2UxMCwgbmNvbD0yKQoKI3JlbW92ZSB0aG9zZSBwYW5lbHMKcm0oc2xpY2UxLCBzbGljZTIsIHNsaWNlMywgc2xpY2U0LCBzbGljZTUsIHNsaWNlNixzbGljZTcsIHNsaWNlOCwgc2xpY2U5LCBzbGljZTEwKQoKYGBgCgoKSm9pbiBMYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGRhdGEKCmBgYHtyfQoKRGVsdGFEYXRhd0xvY2F0aW9ucyA8LSBsZWZ0X2pvaW4oRGVsdGFNYXhNaW4sIERlbHRhTG9jYXRpb25zLCBieSA9IGMoIkRlbHRhIiA9ICJEZWx0YXMiKSkKCkRlbHRhRGF0YXdMb2NhdGlvbnMgPC0gRGVsdGFEYXRhd0xvY2F0aW9ucyAlPiUKICBtdXRhdGUoQWJzb2x1dGVfTGF0aXR1ZGU9IGFicyhMYXQpKQpgYGAKCnBsb3QgcGFyYW1zIHZzIGxhdApgYGB7cn0KbG9jMSA8LSBnZ3Bsb3QoRGVsdGFEYXRhd0xvY2F0aW9ucywgYWVzKHkgPSBBYnNvbHV0ZV9MYXRpdHVkZSwgeCA9IE1heE9mZnNldCkpICsgZ2VvbV9wb2ludCgpIApsb2MyIDwtIGdncGxvdChEZWx0YURhdGF3TG9jYXRpb25zLCBhZXMoeSA9IEFic29sdXRlX0xhdGl0dWRlLCB4ID0gcmFuZ2VORFNTSSkpICsgZ2VvbV9wb2ludCgpIApsb2MzIDwtIGdncGxvdChEZWx0YURhdGF3TG9jYXRpb25zLCBhZXMoeSA9IEFic29sdXRlX0xhdGl0dWRlLCB4ID0gcmFuZ2VORFZJKSkgKyBnZW9tX3BvaW50KCkgCmxvYzQgPC0gZ2dwbG90KERlbHRhRGF0YXdMb2NhdGlvbnMsIGFlcyh5ID0gQWJzb2x1dGVfTGF0aXR1ZGUsIHggPSBNYXhNZWFuTkRWSSkpICsgZ2VvbV9wb2ludCgpIApsb2M1IDwtIGdncGxvdChEZWx0YURhdGF3TG9jYXRpb25zLCBhZXMoeSA9IEFic29sdXRlX0xhdGl0dWRlLCB4ID0gTWF4TWVhbk5EU1NJKSkgKyBnZW9tX3BvaW50KCkgCgpncmlkLmFycmFuZ2UobG9jMSwgbG9jMiwgbG9jMywgbG9jNCwgbG9jNSwgbmNvbD0yKQoKbG9jMQpnZ3NhdmUoImxvYzEucGRmIiwgd2lkdGggPSA0LCBoZWlnaHQgPSA0KQpsb2MyCmdnc2F2ZSgibG9jMi5wZGYiLCB3aWR0aCA9IDQsIGhlaWdodCA9IDQpCmxvYzMKZ2dzYXZlKCJsb2MzLnBkZiIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gNCkKCiNyZW1vdmUgdGhvc2UgcGFuZWxzCnJtKGxvYzEsIGxvYzIsIGxvYzMsIGxvYzQsIGxvYzUpCmBgYAoKYGBge3J9CiNmaW5kIHRoZSBsaW5lYXIgbW9kZWwgCgpEZWx0YU9mZnNldF9sbSA8LSBsbSggQWJzb2x1dGVfTGF0aXR1ZGUgfiBNYXhPZmZzZXQsIGRhdGEgPSBEZWx0YURhdGF3TG9jYXRpb25zKSAKCnN1bW1hcnkoRGVsdGFPZmZzZXRfbG0pCgpnZ3Bsb3QoRGVsdGFEYXRhd0xvY2F0aW9ucywgYWVzKHggPSBBYnNvbHV0ZV9MYXRpdHVkZSwgeSA9IE1heE9mZnNldCwgY29sb3IgPSByYW5nZU5EU1NJKSkgKyBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJ5ZWxsb3ciLCBoaWdoID0gInJlZCIsIG5hLnZhbHVlID0gTkEpICsgCiAgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4ID0gQWJzb2x1dGVfTGF0aXR1ZGUsIHkgPSBNYXhPZmZzZXQsICksIG1ldGhvZD1sbSApIAoKZ2dzYXZlKCJORFNTSW9mZnNldC5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCgpnZ3Bsb3QoRGVsdGFEYXRhd0xvY2F0aW9ucywgYWVzKHggPSBBYnNvbHV0ZV9MYXRpdHVkZSwgeSA9IE1heE9mZnNldCwgY29sb3IgPSByYW5nZU5EVkkpKSArIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gInllbGxvdyIsIGhpZ2ggPSAicmVkIiwgbmEudmFsdWUgPSBOQSkgKyAKICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBBYnNvbHV0ZV9MYXRpdHVkZSwgeSA9IE1heE9mZnNldCwgKSwgbWV0aG9kPWxtICkgCgpnZ3NhdmUoIk5EVklvZmZzZXQucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQoKYGBgCgoKTm93IGZvciB0aGUgbWFwczoKCmBgYHtyfQp3b3JsZCA8LSBnZ3Bsb3QoKSArCiAgYm9yZGVycygid29ybGQiLCBjb2xvdXIgPSAiZ3JheTg1IiwgZmlsbCA9ICJncmF5ODAiKSArCiAgdGhlbWVfbWFwKCkgCgpEZWx0YU9mZnNldE1hcCA8LSB3b3JsZCArCiAgZ2VvbV9wb2ludChhZXMoeCA9IExvbiwgeSA9IExhdCwgY29sb3IgPSBNYXhPZmZzZXQpLAogICAgICAgICAgICAgZGF0YSA9IERlbHRhRGF0YXdMb2NhdGlvbnMsIAogICAgICAgICAgICAgc2l6ZSA9IDUpICsgc2NhbGVfY29sb3JfZ3JhZGllbnQoIGhpZ2ggPSAicmVkIiwgbG93ICA9ICJ5ZWxsb3ciKSArCiAgZ2d0aXRsZSgiT2Zmc2V0IEJldHdlZW4gTkRWSSBwZWFrIG9uIExhbmQgYW5kIE5EU1NJIHBlYWsgaW4gd2F0ZXIiKQoKRGVsdGFPZmZzZXRNYXAKZ2dzYXZlKCJEZWx0YU9mZnNldE1hcC5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCgpgYGAKCgpgYGB7cn0Kd29ybGQgPC0gZ2dwbG90KCkgKwogIGJvcmRlcnMoIndvcmxkIiwgY29sb3VyID0gImdyYXk4NSIsIGZpbGwgPSAiZ3JheTgwIikgKwogIHRoZW1lX21hcCgpIAoKRGVsdGFORFZJcmFuZ2VNYXAgPC0gd29ybGQgKwogIGdlb21fcG9pbnQoYWVzKHggPSBMb24sIHkgPSBMYXQsIGNvbG9yID0gcmFuZ2VORFZJKSwKICAgICAgICAgICAgIGRhdGEgPSBEZWx0YURhdGF3TG9jYXRpb25zLAogICAgICAgICAgICAgc2l6ZSA9IDUpICsgc2NhbGVfY29sb3JfZ3JhZGllbnQoIGhpZ2ggPSAicmVkIiwgbG93ICA9ICJ5ZWxsb3ciKSArIAogIGdndGl0bGUoIk5EVkkgcmFuZ2UiKQoKCkRlbHRhTkRTU0lyYW5nZU1hcCAgPC0gd29ybGQgKwogIGdlb21fcG9pbnQoYWVzKHggPSBMb24sIHkgPSBMYXQsIGNvbG9yID0gcmFuZ2VORFNTSSksCiAgICAgICAgICAgICBkYXRhID0gRGVsdGFEYXRhd0xvY2F0aW9ucywgCiAgICAgICAgICAgICBzaXplID0gNSkgKyBzY2FsZV9jb2xvcl9ncmFkaWVudCggaGlnaCA9ICJyZWQiLCBsb3cgPSAieWVsbG93IikgKyBnZ3RpdGxlKCJORFNTSSByYW5nZSIpIAoKRGVsdGFORFZJcmFuZ2VNYXAgCmdnc2F2ZSgiRGVsdGFORFZJcmFuZ2VNYXAucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQpEZWx0YU5EU1NJcmFuZ2VNYXAKZ2dzYXZlKCJEZWx0YU5EU1NJcmFuZ2VNYXAucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQoKYGBgCgpMZXQncyBsb29rIGF0IHNvbWUgb2YgdGhlIGRhdGEuIApUbyBxdWFudGlmeSB0aGUgd2F0ZXIsIHdlIHVzZSBORFNTSS4gdG8gcXVhbnRpZnkgbGFuZCwgd2UgdXNlIE5EVkkuaGVyZSBpcyB0aGUgdGltZSBzZXJpZXMgZm9yIHRoZSBQYXJhbmEuIAoKYGBge3J9CkRlbHRhUGxvdHRlciA8LSBmdW5jdGlvbihEZWx0YU5hbWUpIHsKICAjQ291bnRzIGVhY2ggbW9udGgKICBudW1WZWcgPC0gRGVsdGFzQ2xlYW5lciAlPiUKICAgIHNlbGVjdChEZWx0YSwgc3VyZmFjZSwgbW9udGgsIG5kdmkpICU+JQogICAgZmlsdGVyKERlbHRhID09IERlbHRhTmFtZSAmIHN1cmZhY2UgPT0gIkxhbmQiICYgIWlzLm5hKG5kdmkpKSAlPiUKICAgIGdyb3VwX2J5KG1vbnRoKSAlPiUKICAgIHN1bW1hcml6ZShuID0gbigpKQogIAogIG51bVNlZCA8LSBEZWx0YXNDbGVhbmVyICU+JQogICAgc2VsZWN0KERlbHRhLCBzdXJmYWNlLCBtb250aCwgbmRzc2kpICU+JQogICAgZmlsdGVyKERlbHRhID09IERlbHRhTmFtZSAmCiAgICAgICAgICAgICBzdXJmYWNlID09ICJXYXRlciIgJiAhaXMubmEobmRzc2kpKSAlPiUKICAgIGdyb3VwX2J5KG1vbnRoKSAlPiUKICAgIHN1bW1hcml6ZShuID0gbigpKQogIAogICNIaWdobGlnaHQgdGhlIE1heGltdW0gYW5kIE1pbmltdW0gTW9udGggZm9yIGVhY2ggZGVsdGEsIE5EVkkgYW5kIE5EU1NJCiAgCiAgI0xBTkQKICBWZWcgPC0KICAgIGdncGxvdChkYXRhID0gZmlsdGVyKERlbHRhc0NsZWFuZXIsIERlbHRhID09IERlbHRhTmFtZSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cmZhY2UgPT0gIkxhbmQiKSkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gbW9udGgsIHkgPSBuZHZpLCBncm91cCA9IG1vbnRoKSkgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6MTIpLCBicmVha3MgPSBjKDE6MTIpKSArCiAgICBleHBhbmRfbGltaXRzKHggPSBjKDEsIDEyKSwgeSA9IGMoMCwgMSkpICsKICAgIGdndGl0bGUoRGVsdGFOYW1lKSArCiAgICBnZW9tX3RleHQoZGF0YSA9IG51bVZlZywgYWVzKHkgPSAxLjA1LCB4ID0gbW9udGgsIGxhYmVsID0gbikpICsKICAgIGdlb21fYm94cGxvdCgKICAgICAgZGF0YSA9IGZpbHRlcigKICAgICAgICBEZWx0YXNDbGVhbmVyLAogICAgICAgIERlbHRhID09IERlbHRhTmFtZSAmCiAgICAgICAgICBzdXJmYWNlID09ICJMYW5kIiAmIG1vbnRoID09IERlbHRhTWF4TWluJE1heE1lYW5ORFZJbW9udGhbRGVsdGFNYXhNaW4kRGVsdGEgPT0gRGVsdGFOYW1lXSAKICAgICAgKSwKICAgICAgYWVzKHggPSBtb250aCwgeSA9IG5kdmksIGdyb3VwID0gbW9udGgpLAogICAgICBmaWxsID0gImdyZWVuIgogICAgKSArCiAgICBnZW9tX2JveHBsb3QoCiAgICAgIGRhdGEgPSBmaWx0ZXIoCiAgICAgICAgRGVsdGFzQ2xlYW5lciwKICAgICAgICBEZWx0YSA9PSBEZWx0YU5hbWUgJiAKICAgICAgICAgIHN1cmZhY2UgPT0gIkxhbmQiICYgbW9udGggPT0gRGVsdGFNYXhNaW4kTWluTWVhbk5EVkltb250aFtEZWx0YU1heE1pbiREZWx0YSA9PURlbHRhTmFtZV0KICAgICAgKSwKICAgICAgYWVzKHggPSBtb250aCwgeSA9IG5kdmksIGdyb3VwID0gbW9udGgpLAogICAgICBmaWxsID0gImJsdWUiCiAgICApCiAgCiAgCiAgU2VkIDwtCiAgICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcihEZWx0YXNDbGVhbmVyLCBEZWx0YSA9PSBEZWx0YU5hbWUgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJmYWNlID09ICJXYXRlciIpKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKHggPSBtb250aCwgeSA9IG5kc3NpLCBncm91cCA9IG1vbnRoKSkgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKDE6MTIpLCBicmVha3MgPSBjKDE6MTIpKSArCiAgICBleHBhbmRfbGltaXRzKHggPSBjKDEsIDEyKSwgeSA9IGMoMCwgMSkpICsKICAgIGdlb21fdGV4dChkYXRhID0gbnVtU2VkLCBhZXMoeSA9IDEuMDUsIHggPSBtb250aCwgbGFiZWwgPSBuKSkgKwogICAgZ2VvbV9ib3hwbG90KAogICAgICBkYXRhID0gZmlsdGVyKAogICAgICAgIERlbHRhc0NsZWFuZXIsCiAgICAgICAgRGVsdGEgPT0gRGVsdGFOYW1lICYKICAgICAgICAgIHN1cmZhY2UgPT0gIldhdGVyIiAmIG1vbnRoID09IERlbHRhTWF4TWluJE1heE1lYW5ORFNTSW1vbnRoW0RlbHRhTWF4TWluJERlbHRhID09IERlbHRhTmFtZV0KICAgICAgKSwKICAgICAgYWVzKHggPSBtb250aCwgeSA9IG5kc3NpLCBncm91cCA9IG1vbnRoKSwKICAgICAgZmlsbCA9ICJncmVlbiIKICAgICkgKwogICAgZ2VvbV9ib3hwbG90KAogICAgICBkYXRhID0gZmlsdGVyKAogICAgICAgIERlbHRhc0NsZWFuZXIsCiAgICAgICAgRGVsdGEgPT0gRGVsdGFOYW1lICYKICAgICAgICAgIHN1cmZhY2UgPT0gIldhdGVyIiAmIG1vbnRoID09IERlbHRhTWF4TWluJE1pbk1lYW5ORFNTSW1vbnRoW0RlbHRhTWF4TWluJERlbHRhID09IERlbHRhTmFtZV0KICAgICAgKSwKICAgICAgYWVzKHggPSBtb250aCwgeSA9IG5kc3NpLCBncm91cCA9IG1vbnRoKSwKICAgICAgZmlsbCA9ICJibHVlIgogICAgKQogIAogIHJldHVybihncmlkLmFycmFuZ2UoVmVnLCBTZWQsIG5yb3cgPSAyKSkKfQpgYGAKCgpIZXJlIGlzIGFuICdPZmZzZXQnIGV4YW1wbGUgZnJvbSB0aGUgUGFyYW5hOgpgYGB7cn0KRGVsdGFQbG90dGVyKCJQYXJhbmEiKQpgYGAKCgphbmQgbm93IGFuIGluLXBoYXNlIGV4YW1wbGUgZnJvbQpgYGB7cn0KRGVsdGFQbG90dGVyKCJNYWdkYWxlbmEiKQpgYGAKCgphbmQgdGhlIEVicm86CmBgYHtyfQpEZWx0YVBsb3R0ZXIoIktyaXNobmEiKQpgYGAKCgphbmQgdGhlIFRhbmEKYGBge3J9CkRlbHRhUGxvdHRlcigiVGFuYSIpCmBgYAoKYW5kIHRoZSBUYW5hCmBgYHtyfQpEZWx0YVBsb3R0ZXIoIkdvZGF2YXJpIikKRGVsdGFQbG90dGVyKCJLcmlzaG5hIikKYGBgCgoKCgoKTm93IGZvciBzb21lIHdvcmsgd2l0aCBHUkRDIGRhdGE6CmBgYHtyfQojaW1wb3J0IHRoZSBkYXRhIChtb250aGx5IG1lYW5zIGZvciAyMSBzdGF0aW9ucykKRGVsdGFzR1JEQyAgPC0gcmVhZF9jc3YoIi4uL2RhdGEvR1JEQ3N0YXRpb25zLmNzdiIpCgojY2FsY3VsYXRlIHRoZSBtZWFuIG9mIHRoZSBtb250aGx5IG1lYW5zCkRlbHRhc0dSREN3TWVhbiA8LSBEZWx0YXNHUkRDICU+JSAKICAgIHJvd3dpc2UoKSAlPiUgCiAgICBtdXRhdGUoTU1EPW1lYW4oYyhKYW51YXJ5LEZlYnJ1YXJ5LE1hcmNoLEFwcmlsLAogICAgICAgICAgICAgICAgICAgIE1heSxKdW5lLEp1bHksQXVndXN0LAogICAgICAgICAgICAgICAgICAgIFNlcHRlbWJlcixPY3RvYmVyLE5vdmVtYmVyLERlY2VtYmVyKSkpCgpEZWx0YXNHUkRDd01lYW4gPC0gRGVsdGFzR1JEQ3dNZWFuICU+JSAKICByb3d3aXNlKCkgJT4lIAogIG11dGF0ZShSYW5nZV9EaXNjaGFyZ2UgPSBtYXgoYyhKYW51YXJ5LEZlYnJ1YXJ5LE1hcmNoLEFwcmlsLAogICAgICAgICAgICAgIE1heSxKdW5lLEp1bHksQXVndXN0LAogICAgICAgICAgICAgIFNlcHRlbWJlcixPY3RvYmVyLE5vdmVtYmVyLERlY2VtYmVyKSkgLSAKICAgICAgICAgICBtaW4oYyhKYW51YXJ5LEZlYnJ1YXJ5LE1hcmNoLEFwcmlsLAogICAgICAgICAgICAgIE1heSxKdW5lLEp1bHksQXVndXN0LAogICAgICAgICAgICAgIFNlcHRlbWJlcixPY3RvYmVyLE5vdmVtYmVyLERlY2VtYmVyKSkKICAgICAgICAgICApCiAgICAgICAgCgojam9pbiB0byBsb2NhdGlvbiBkYXRhOgpEZWx0YXdMb2NHUkRDIDwtIGxlZnRfam9pbihEZWx0YURhdGF3TG9jYXRpb25zLCBEZWx0YXNHUkRDd01lYW4sIGJ5ID0gYygiRGVsdGEiID0gIkRlbHRhcyIpKQoKCiNwbG90IG1lYW4gb2YgbW9udGhseSBtZWFucyBhZ2FpbnN0IE5EU1NJCmdncGxvdChEZWx0YXdMb2NHUkRDLCBhZXMoeSA9IFJhbmdlX0Rpc2NoYXJnZSwgeCA9IHJhbmdlTkRTU0kpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nMTAnKQoKZ2dzYXZlKCJHUkRDTkRTU0kucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQoKZ2dwbG90KERlbHRhd0xvY0dSREMsIGFlcyh5ID0gUmFuZ2VfRGlzY2hhcmdlLCB4ID0gTWF4TWVhbk5EU1NJKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykKYGBgCgoKCmBgYHtyfQoKCiNyZW5hbWUgdGhlIG1vbnRocyBieSBudW1iZXJzIGFuZCB0aWR5IHRoZSBHUkRDIGRhdGEKRGVsdGFzRGlzY2hhcmdlIDwtIERlbHRhc0dSREMgJT4lCiAgcmVuYW1lKERlbHRhID0gRGVsdGFzLCIxIiA9IEphbnVhcnksICIyIj0gRmVicnVhcnksICIzIj0gTWFyY2gsICI0Ij0gQXByaWwsCiAgICAgICAgICI1Ij1NYXksICI2Ij1KdW5lLCAiNyI9SnVseSwgIjgiPSBBdWd1c3QsICI5IiA9IFNlcHRlbWJlciwgIjEwIj1PY3RvYmVyLCAKICAgICAgICAgIjExIj1Ob3ZlbWJlciwgIjEyIj1EZWNlbWJlcikgJT4lCiAgc2VsZWN0KERlbHRhLCAiMSIgLCAiMiIgLCAiMyIsICI0IiwiNSIsICI2IiwgIjciLCAiOCIsICI5IiwgIjEwIiwgIjExIiwgIjEyIikgJT4lCiAgcGl2b3RfbG9uZ2VyKC1EZWx0YSwgbmFtZXNfdG8gPSAibW9udGgiLCB2YWx1ZXNfdG8gPSAiZGlzY2hhcmdlIikKCiNmaW5kIG1heCBHUkRDIG1vbnRoIGZvciBlYWNoIGRlbHRhCkRlbHRhTWF4RGlzY2hhcmdlIDwtIAogIERlbHRhc0Rpc2NoYXJnZSAlPiUgCiAgZ3JvdXBfYnkoRGVsdGEpICU+JSAKICBzbGljZSh3aGljaC5tYXgoZGlzY2hhcmdlKSkgJT4lIAogIHJlbmFtZShNYXhEaXNjaGFyZ2VNb250aCA9IG1vbnRoLCBNYXhEaXNjaGFyZ2UgPSBkaXNjaGFyZ2UpIAoKCkRlbHRhTWF4RGlzY2hhcmdlJE1heERpc2NoYXJnZU1vbnRoID0gYXMubnVtZXJpYyhEZWx0YU1heERpc2NoYXJnZSRNYXhEaXNjaGFyZ2VNb250aCkKCgojam9pbiB3aXRoIG90aGVyIGRlbHRhIGRhdGEKRGVsdGFNYXhNaW5EaXNjaGFyZ2UgPC0gbGVmdF9qb2luKERlbHRhTWF4TWluLCBEZWx0YU1heERpc2NoYXJnZSwgYnkgPSAnRGVsdGEnKQoKI2NhbGN1bGF0ZSBvZmZzZXQKRGVsdGFNYXhNaW5EaXNjaGFyZ2UgPC0gRGVsdGFNYXhNaW5EaXNjaGFyZ2UgJT4lCiAgbXV0YXRlKCBEaXNzT2ZmID0gaWZfZWxzZShhYnMoTWF4RGlzY2hhcmdlTW9udGggLSBNYXhNZWFuTkRTU0ltb250aCkgPiA2LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgMTIgLSBhYnMoTWF4RGlzY2hhcmdlTW9udGggLSBNYXhNZWFuTkRTU0ltb250aCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhYnMoTWF4RGlzY2hhcmdlTW9udGggLSBNYXhNZWFuTkRTU0ltb250aCkpCiAgICAgICAgICApCgojQ29tcGFyZSBvZmZzZXQgd2l0aCBORFNTSSAoZGVsdGFtYXhtaW4kbWF4bWVhbk5EU1NJbW9udGgpCmdncGxvdChEZWx0YU1heE1pbkRpc2NoYXJnZSwgYWVzKHkgPSBEZWx0YSwgeCA9IERpc3NPZmYpKSArIGdlb21fcG9pbnQoKSArIAogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygxOjYpLCBicmVha3MgPSBjKDE6NikpICsKICBleHBhbmRfbGltaXRzKHggPSBjKDAsNikpICArIAogIGdndGl0bGUoIkRpc09mZnNldCIpCgoKI2p1c3QgdG8gY2hlY2sKCmNoZWNrIDwtIERlbHRhTWF4TWluRGlzY2hhcmdlICU+JQogIHNlbGVjdChEZWx0YSxNYXhNZWFuTkRWSW1vbnRoLCBNYXhNZWFuTkRTU0ltb250aCwgTWF4RGlzY2hhcmdlTW9udGgsIERpc3NPZmYpCgoKI2pvaW4gbGF0IGRhdGEKRGVsdGFEYXRhd0xvY2F0aW9ucyA8LSBsZWZ0X2pvaW4oRGVsdGFNYXhNaW5EaXNjaGFyZ2UsIERlbHRhTG9jYXRpb25zLCBieSA9IGMoIkRlbHRhIiA9ICJEZWx0YXMiKSkKCkRlbHRhRGF0YXdMb2NhdGlvbnMgPC0gRGVsdGFEYXRhd0xvY2F0aW9ucyAlPiUKICBtdXRhdGUoQWJzb2x1dGVfTGF0aXR1ZGU9IGFicyhMYXQpKQoKIyBwbG90IG9mZnNldCBvbiBncmFwaCBieSBsYXQKZ2dwbG90KERlbHRhRGF0YXdMb2NhdGlvbnMsIGFlcyh4ID0gQWJzb2x1dGVfTGF0aXR1ZGUsIHkgPSBEaXNzT2ZmKSkgKyBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJ5ZWxsb3ciLCBoaWdoID0gInJlZCIsIG5hLnZhbHVlID0gTkEpIAogICMrIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IEFic29sdXRlX0xhdGl0dWRlLCB5ID0gRGlzc09mZiwgKSwgbWV0aG9kPWxtICkgCgpnZ3NhdmUoIkRpc09mZnNldC5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCgojcGxvdCBvZmZzZXQgb24gbWFwCkRlbHRhRGlzT2Zmc2V0TWFwIDwtIHdvcmxkICsKICBnZW9tX3BvaW50KGFlcyh4ID0gTG9uLCB5ID0gTGF0LCBjb2xvciA9IERpc3NPZmYpLAogICAgICAgICAgICAgZGF0YSA9IERlbHRhRGF0YXdMb2NhdGlvbnMsIAogICAgICAgICAgICAgc2l6ZSA9IDUpICsgc2NhbGVfY29sb3JfZ3JhZGllbnQoIGhpZ2ggPSAicmVkIiwgbG93ICA9ICJ5ZWxsb3ciKSArCiAgZ2d0aXRsZSgiT2Zmc2V0IEJldHdlZW4gR1JEQyBkaXNjaGFyZ2UgcGVhayBhbmQgTkRTU0kgcGVhayBpbiB3YXRlciIpCgpEZWx0YURpc09mZnNldE1hcAojZ2dzYXZlKCJEZWx0YU9mZnNldE1hcC5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCmBgYAoKCgoK